//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ClusterByAPALINGO.h"
#include "AncestorMol.h"
#include "ListAncestorMol.h"
#include "ProcessFileParallel.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <fstream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//Constanst to generate the deep of ancestor
#define NANCESTOR    4
#define NITERATION   50
#define PERCENTCONV  0.95
#define LINGOLENGTH  4
#define RANDOMFILE   1
#define LENGHTINLINE 10000

//Constanst to create the cluster in algorimths
#define	ARRAYSIZE    600000
#define CLUST_SIZE   1000
#define ITERATIONS   1
#define SHARED       1
#define BUFFCICLE    10000
#define LENGHTBLINE  40
#define NONZERO      0
#define MEDIAN       -0.946667
#define USE_MEDIAN   0
#define USE_DISTANCE 1
#define USE_PREFEREN 1
#define USE_MATLAB   1

#define MAXITS_1     500
#define MAXITS_2     1000
#define CONVITS_1    50
#define CONVITS_2    100
#define DAMFACT_1    0.5
#define DAMFACT_2    0.9

//Name of APA output file
#define APSTOPFILE   ap.stop
#define IDXOUTFILE   idx.txt
#define NETSIM2FILE  netsim2.txt
#define NETSIMFILE   netsim.txt
#define PREFFILE     pref.txt
#define SUMMARYFILE  summary.txt

#define SIZENAMEFILE 1000

#define NAMECLUSTERT "Cluster_Molecules.txt"
#define NAMEDBSMITMP "DB_Molecule_TMP.tmp"
#define FORMATRESULT "tmp_Result%05d.txt\0"
#define FORMATCLUST  "Cluster_Molecules%03d.txt\0"

#define ORDERWAY     "CENTERED" //"CENTERED" "ASCENDENT" "DESCENDENT"
#define READFILE     1
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ClusterByAPALINGO::ClusterByAPALINGO() {

  this->initialTime     = 0;
  this->finalTime       = 0;

  this->err             = 0;
  this->rank            = 0;
  this->sizeRank        = 0;

  this->molDB           = new ListSmile();

  this->filename        = new String();
  this->initialFileName = new String();
  this->nameLogFile     = new String();

  this->convergence     = false;
  this->condLogFile     = true;

  this->maxIterConv     = 25;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ClusterByAPALINGO::~ClusterByAPALINGO() {
  
  if (this->molDB)            delete this->molDB;
  if (this->filename)         delete this->filename;
  if (this->initialFileName)  delete this->initialFileName;
  if (this->nameLogFile)      delete this->nameLogFile;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::InitialTime(time_t initialTime) {
  this->initialTime = initialTime;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::FinalTime(time_t finalTime) {
  this->finalTime = finalTime;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::MolDB(ListSmile* molDB) {
  *(this->molDB) = *(molDB);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::Filename(String* filename) {
  *(this->filename) = *(filename);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::NameLogFile(String* nameLogFile) {
  *(this->nameLogFile) = *(nameLogFile);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::CondLogFile(bool condLogFile) {
  this->condLogFile = condLogFile;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::MaxIterConv(int maxIterConv) {
  this->maxIterConv = (maxIterConv > 0)?maxIterConv:25;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
time_t ClusterByAPALINGO::InitialTime() {
  return this->initialTime;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
time_t ClusterByAPALINGO::FinalTime() {
  return this->finalTime;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ListSmile* ClusterByAPALINGO::MolDB() {
  return this->molDB;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ClusterByAPALINGO::Filename() {
  return this->filename;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ClusterByAPALINGO::InitialFileName() {
  return this->initialFileName;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ClusterByAPALINGO::NameLogFile() {
  return this->nameLogFile;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ClusterByAPALINGO::CondLogFile() {
  return this->condLogFile;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::Err(int err) {
  this->err = err;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::Rank(int rank) {
  this->rank = rank;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ClusterByAPALINGO::SizeRank(int sizeRank) {
  this->sizeRank = sizeRank;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ClusterByAPALINGO::Err() {
  return this->err;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ClusterByAPALINGO::Rank() {
  return this->rank;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ClusterByAPALINGO::SizeRank() {
  return this->sizeRank;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ClusterByAPALINGO::MaxIterConv() {
  return this->maxIterConv;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
MPI_Request ClusterByAPALINGO::Reqs(int pos) {
  return this->reqs[pos];
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
MPI_Status ClusterByAPALINGO::Stat() {
  return this->stat;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ClusterByAPALINGO::ClusteringAPA(long totalNumberMol) {

  int initialPos;                                                               //Initial Position to read the Smiles in the original File
  int finalPos;                                                                 //Final   Position to read the Smiles in the original File
  int numberPart      = totalNumberMol/CLUST_SIZE;                              //Number Partition to compute the clustering
  int iter            = 1;                                                      //Number of Iteration to complete the clustering segment
  int posPart         = this->rank;                                             //Current segment Position (Partitions)
  int numberClusters  = 0;

  char    timeText[300];

  numberPart += (totalNumberMol%CLUST_SIZE)?1:0;

  if (((numberPart == 1) && this->rank) || (posPart > numberPart - 1)) return numberClusters;

  fstream fileResult;
  char    resultFile[SIZENAMEFILE];

  sprintf(resultFile, FORMATRESULT, this->rank);
  fileResult.open(resultFile, fstream::out);

  if (!fileResult.is_open()) return numberClusters;

  while (posPart < numberPart) {

    //Compute the initial and final position to read in the dataset
    initialPos = posPart*CLUST_SIZE + 1;
    finalPos   = ((posPart + 1) < numberPart)?((posPart + 1)*CLUST_SIZE):totalNumberMol;

    if (!this->molDB->ExtractSmiles(initialPos, finalPos - initialPos + 1))           //Extract only the molecules that will process in this iterations
      return 0;

    if (this->molDB->AllSmiles()->Length() > 2) {

      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Computing the Similarity Matrix " << endl;
      if (!this->molDB->ObtainDistanceBySimilarityMatrixWithoutNumber()) return 0;    //Compute the similarity Matrix for the selected molecules

      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Computing the Preference value " << endl;
      if (!this->molDB->ObtainAverageDistanceBySimMatrixWithoutNumber()) return 0;    //Obtain the average of LingoSim for each molecule

      APCluster *apCluster = new APCluster();                                         //Object to compute the cluster with APA
      if (!apCluster) {
        cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Error Allocating Memory " << endl;
        fileResult.close();
        return 0;
      }

      //Load in the apCluster object the Similarity Matrix and the Preference Vector and set the parameters values
      apCluster->PreferencePoint(this->molDB->AverageSimDistance(), this->molDB->AllSmiles()->Length());
      apCluster->DataPoints(this->molDB->LingoSimilarity(), this->molDB->AllSmiles()->Length());
      apCluster->Noise(false);

      apCluster->Dampfact(0.5);
      apCluster->Convits(50);
      apCluster->Maxits(500);

      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Clustering with APA " << endl;
      //Execute the affinity propagation algorithm
      if (!apCluster->ComputeAPCluster(this->rank)) {
        cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Error with the DataPoint or Preferences values " << endl;
        if (apCluster) delete apCluster;
        fileResult.close();
        return 0;
      }

      //If not converge change tha parameters value and execute the APA again
      if (!apCluster->ConvCluster()) {
        apCluster->Dampfact(0.9);
        apCluster->Convits(100);
        apCluster->Maxits(1000);

        if (!apCluster->ComputeAPCluster(this->rank)) {
          cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Error with the DataPoint or Preferences values " << endl;
          if (apCluster) delete apCluster;
          fileResult.close();
          return 0;
        }

      }
      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Finished the Clustering with APA " << endl;

      //Store the trace of exemplar for the complete dataset
      for (long i = 0; i < this->molDB->AllSmiles()->Length(); i++) {
        fileResult << left;
        fileResult.width(12);
        if (apCluster->ConvCluster()) fileResult << this->molDB->AllSmiles()->Get(i)->Id() << "  " << this->molDB->AllSmiles()->Get(apCluster->Idx()[i])->Id() << endl;
        else                          fileResult << this->molDB->AllSmiles()->Get(i)->Id() << "  " << this->molDB->AllSmiles()->Get(i)->Id() << endl;
      }

      numberClusters += apCluster->NumberClusters();

      if (apCluster) delete apCluster;

    }
    else {

      for (long i = 0; i < this->molDB->AllSmiles()->Length(); i++) {
        fileResult << left;
        fileResult.width(12);
        fileResult << this->molDB->AllSmiles()->Get(i)->Id() << "  " << this->molDB->AllSmiles()->Get(i)->Id() << endl;
      }

      numberClusters = this->molDB->AllSmiles()->Length();

    }

    posPart = ((iter++)*this->sizeRank) + this->rank;

  }

  fileResult.close();

  return numberClusters;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ClusterByAPALINGO::IterativeClusteringAPA(long totalNumberMol) {
  
  int initialPos;                                                               //Initial Position to read the Smiles in the original File
  int finalPos;                                                                 //Final   Position to read the Smiles in the original File
  int numberPart      = totalNumberMol/CLUST_SIZE;                              //Number Partition to compute the clustering
  int iter            = 1;                                                      //Number of Iteration to complete the clustering segment
  int posPart         = this->rank;                                             //Current segment Position (Partitions)
  int numberClusters  = 0;

  char    timeText[300];

  numberPart += (totalNumberMol%CLUST_SIZE)?1:0;

  if (((numberPart == 1) && this->rank) || (posPart > numberPart - 1)) return numberClusters;

  fstream fileResult;
  char    resultFile[SIZENAMEFILE];
  
  sprintf(resultFile, FORMATRESULT, this->rank);
  fileResult.open(resultFile, fstream::out);

  if (!fileResult.is_open()) return numberClusters;

  while (posPart < numberPart) {

    //Compute the initial and final position to read in the dataset
    initialPos = posPart*CLUST_SIZE + 1;
    finalPos   = ((posPart + 1) < numberPart)?((posPart + 1)*CLUST_SIZE):totalNumberMol;

    if (!this->molDB->ExtractSmiles(initialPos, finalPos - initialPos + 1))           //Extract only the molecules that will process in this iterations
      return 0;
    
    if (this->molDB->AllSmiles()->Length() > 2) {
      
      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Computing the Similarity Matrix " << endl;
      if (!this->molDB->ObtainDistanceBySimilarityMatrixWithoutNumber()) return 0;    //Compute the similarity Matrix for the selected molecules

      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Computing the Preference value " << endl;
      if (!this->molDB->ObtainAverageDistanceBySimMatrixWithoutNumber()) return 0;    //Obtain the average of LingoSim for each molecule

      APCluster *apCluster = new APCluster();                                         //Object to compute the cluster with APA
      if (!apCluster) {
        cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Error Allocating Memory " << endl;
        fileResult.close(); 
        return 0;
      }

      //Load in the apCluster object the Similarity Matrix and the Preference Vector and set the parameters values
      apCluster->PreferencePoint(this->molDB->AverageSimDistance(), this->molDB->AllSmiles()->Length());
      apCluster->DataPoints(this->molDB->LingoSimilarity(), this->molDB->AllSmiles()->Length());
      apCluster->Noise(false);
      
      apCluster->Dampfact(0.5);
      apCluster->Convits(50);
      apCluster->Maxits(500);

      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Clustering with APA " << endl;
      //Execute the affinity propagation algorithm
      if (!apCluster->ComputeAPCluster(this->rank)) {
        cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Error with the DataPoint or Preferences values " << endl;
        if (apCluster) delete apCluster;
        fileResult.close();
        return 0;
      }

      //If not converge change tha parameters value and execute the APA again
      if (!apCluster->ConvCluster()) {
        apCluster->Dampfact(0.9);
        apCluster->Convits(100);
        apCluster->Maxits(1000);

        if (!apCluster->ComputeAPCluster(this->rank)) {
          cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Error with the DataPoint or Preferences values " << endl;
          if (apCluster) delete apCluster;
          fileResult.close();
          return 0;
        }

      }
      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Finished the Clustering with APA " << endl;

      //Store the trace of exemplar for the complete dataset
      ListAncestorMol* listOutAncestorMol = new ListAncestorMol();

      for (long i = 0; i < this->molDB->AllSmiles()->Length(); i++) {

        AncestorMol* ancestorMol = new AncestorMol();
        String*      IdChildren  = new String();
        String*      IdFather    = new String();

        IdChildren->In(this->molDB->AllSmiles()->Get(i)->Id());
        IdFather->In((apCluster->ConvCluster())?(this->molDB->AllSmiles()->Get(apCluster->Idx()[i])->Id()):(this->molDB->AllSmiles()->Get(i)->Id()));

        ancestorMol->ListIdAncestor()->Add(IdChildren);
        ancestorMol->ListIdAncestor()->Add(IdFather);

        listOutAncestorMol->ListAncestor()->Add(ancestorMol);
        
      }

      if      (!strcmp(ORDERWAY, "CENTERED"))   listOutAncestorMol->OrderListByAncestorCenteredIterative();
      else if (!strcmp(ORDERWAY, "ASCENDENT"))  listOutAncestorMol->OrderListByAncestorAscendentIterative();
      else if (!strcmp(ORDERWAY, "DESCENDENT")) listOutAncestorMol->OrderListByAncestorDescendentIterative();

      listOutAncestorMol->ListAncestor()->SetInitial();

      while (listOutAncestorMol->ListAncestor()->SetNext()) {
        AncestorMol* ancestorMol = listOutAncestorMol->ListAncestor()->GetCurrent();
        ancestorMol->ListIdAncestor()->SetInitial();
        while (ancestorMol->ListIdAncestor()->SetNext()) {
          fileResult << left; fileResult.width(12);
          fileResult << ancestorMol->ListIdAncestor()->GetCurrent()->Out() << " ";
        }
        fileResult << endl;
      }

      if (listOutAncestorMol) delete listOutAncestorMol;

      numberClusters += apCluster->NumberClusters();

      if (apCluster) delete apCluster;

    }
    else {
      
      for (long i = 0; i < this->molDB->AllSmiles()->Length(); i++) {
        fileResult << left;
        fileResult.width(12);
        fileResult << this->molDB->AllSmiles()->Get(i)->Id() << "  " << this->molDB->AllSmiles()->Get(i)->Id() << endl;
      }

      numberClusters = this->molDB->AllSmiles()->Length();
      
    }

    posPart = ((iter++)*this->sizeRank) + this->rank;

  }
  
  fileResult.close();

  return numberClusters;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ClusterByAPALINGO::JoinClusterResult(char *inputNameFile, int nAncestor) {

  if (!inputNameFile || nAncestor < 0) return 0;

  fstream               inputFile;
  fstream               clusterFile;

  char                  line[LENGHTINLINE + 1];

  TListE <AncestorMol>* listAncestorNew       = new TListE <AncestorMol> ();
  TListE <AncestorMol>* listAncestorStored    = new TListE <AncestorMol> ();
  
  ListAncestorMol*      outlistAncestorStored = new ListAncestorMol();
  
  inputFile.open(inputNameFile, fstream::in);
  inputFile.seekg(0, fstream::beg);

  //Read Input_File with the new Cluster assignment
  while (!inputFile.eof()) {
    
    inputFile.getline(line, LENGHTINLINE);
    String inputLine; inputLine.In(line); inputLine.Trim();

    if (!inputLine.Length()) continue;

    AncestorMol* ancestor = new AncestorMol();
    ancestor->InputLineAncestor(inputLine);
    
    listAncestorNew->Add(ancestor);
    
  }
  
  if (!nAncestor) {
    outlistAncestorStored->ListAncestor(listAncestorNew);
  }
  else if (nAncestor > 0) {

    clusterFile.open(NAMECLUSTERT, fstream::in);
    clusterFile.seekg(0, fstream::beg);

    //Read the stored Cluster_Molecules.txt
    while (!clusterFile.eof()) {
      clusterFile.getline(line, LENGHTINLINE);
      String inputLine; inputLine.In(line); inputLine.Trim();
      if (!inputLine.Length()) continue;
      AncestorMol* ancestor = new AncestorMol();
      ancestor->InputLineAncestor(inputLine);
      listAncestorStored->Add(ancestor);
    }

    clusterFile.close();
    
    //Complete Cluster_Molecule
    listAncestorStored->SetInitial();
    
    while (listAncestorStored->SetNext()) {
      
      bool condition = false;
      
      listAncestorNew->SetInitial();
      
      while (listAncestorNew->SetNext()) {
        if (!strcmp(listAncestorStored->GetCurrent()->ListIdAncestor()->GetFinal()->Out(), listAncestorNew->GetCurrent()->ListIdAncestor()->GetFirst()->Out())) {
          String* newLevelAncestor = new String(listAncestorNew->GetCurrent()->ListIdAncestor()->GetFinal());
          condition                = true;
          listAncestorStored->GetCurrent()->ListIdAncestor()->Add(newLevelAncestor);
          break;
        }
      }

      if (!condition) {
        String* newLevelAncestor = new String(listAncestorStored->GetCurrent()->ListIdAncestor()->GetFinal());
        listAncestorStored->GetCurrent()->ListIdAncestor()->Add(newLevelAncestor);
      }


    }
    
    outlistAncestorStored->ListAncestor(listAncestorStored);

  }

  outlistAncestorStored->FilenameSmiles(this->initialFileName);
  outlistAncestorStored->LoadDataSmilesFiles();

  if      (!strcmp(ORDERWAY, "CENTERED"))   outlistAncestorStored->OrderListByAncestorCenteredIterative();
  else if (!strcmp(ORDERWAY, "ASCENDENT"))  outlistAncestorStored->OrderListByAncestorAscendentIterative();
  else if (!strcmp(ORDERWAY, "DESCENDENT")) outlistAncestorStored->OrderListByAncestorDescendentIterative();

  clusterFile.open(NAMECLUSTERT, fstream::out);
  outlistAncestorStored->ListAncestor()->SetInitial();

  while (outlistAncestorStored->ListAncestor()->SetNext()) {

    AncestorMol* currentAncestor = outlistAncestorStored->ListAncestor()->GetCurrent();
    currentAncestor->ListIdAncestor()->SetInitial();

    while (currentAncestor->ListIdAncestor()->SetNext()) {
      clusterFile << left;
      clusterFile.width(12);
      clusterFile << currentAncestor->ListIdAncestor()->GetCurrent()->Out() << " ";
    }

    clusterFile << endl;

  }

  outlistAncestorStored->ListAncestor()->TransferAll();
  
  clusterFile.close();
  
  if (listAncestorNew)        delete listAncestorNew;
  if (listAncestorStored)     delete listAncestorStored;
  if (outlistAncestorStored)  delete outlistAncestorStored;

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ClusterByAPALINGO::JoinIterativeClusterResult(char *inputNameFile, int nAncestor) {

  if (!inputNameFile || nAncestor < 0) return 0;

  fstream               inputFile;
  fstream               clusterFile;

  char                  line[LENGHTINLINE + 1];

  TListE <AncestorMol>* listAncestorNew       = new TListE <AncestorMol> ();
  ListAncestorMol*      outlistAncestorStored = new ListAncestorMol();

  inputFile.open(inputNameFile, fstream::in);
  inputFile.seekg(0, fstream::beg);

  //Read Input_File with the new Cluster assignment
  while (!inputFile.eof()) {

    inputFile.getline(line, LENGHTINLINE);
    String inputLine; inputLine.In(line); inputLine.Trim();

    if (!inputLine.Length()) continue;

    AncestorMol* ancestor = new AncestorMol();
    ancestor->InputLineAncestor(inputLine);

    if (nAncestor) listAncestorNew->Add(ancestor);
    else           outlistAncestorStored->ListAncestor()->Add(ancestor);

  }

  if (nAncestor) {

    TListE <AncestorMol>* listAncestorStored    = new TListE <AncestorMol> ();
    
    clusterFile.open(NAMECLUSTERT, fstream::in);
    clusterFile.seekg(0, fstream::beg);

    //Read the stored Cluster_Molecules.txt
    while (!clusterFile.eof()) {
      clusterFile.getline(line, LENGHTINLINE);
      String inputLine; inputLine.In(line); inputLine.Trim();
      if (!inputLine.Length()) continue;
      AncestorMol* ancestor = new AncestorMol();
      ancestor->InputLineAncestor(inputLine);
      listAncestorStored->Add(ancestor);
    }

    clusterFile.close();

    //Complete Cluster_Molecule
    listAncestorStored->SetInitial();

    while (listAncestorStored->SetNext()) {

      bool condition = false;

      listAncestorNew->SetInitial();

      while (listAncestorNew->SetNext()) {
        if (!strcmp(listAncestorStored->GetCurrent()->ListIdAncestor()->GetFinal()->Out(), listAncestorNew->GetCurrent()->ListIdAncestor()->GetFirst()->Out())) {
          String* newLevelAncestor = new String(listAncestorNew->GetCurrent()->ListIdAncestor()->GetFinal());
          condition                = true;
          listAncestorStored->GetCurrent()->ListIdAncestor()->Add(newLevelAncestor);
          break;
        }
      }

      if (!condition) {
        String* newLevelAncestor = new String(listAncestorStored->GetCurrent()->ListIdAncestor()->GetFinal());
        listAncestorStored->GetCurrent()->ListIdAncestor()->Add(newLevelAncestor);
      }


    }

    outlistAncestorStored->ListAncestor(listAncestorStored);

    if (listAncestorStored) delete listAncestorStored;
    
  }

  outlistAncestorStored->FilenameSmiles(this->initialFileName);
  outlistAncestorStored->LoadDataSmilesFiles();

  if      (!strcmp(ORDERWAY, "CENTERED"))   outlistAncestorStored->OrderListByAncestorCentered();
  else if (!strcmp(ORDERWAY, "ASCENDENT"))  outlistAncestorStored->OrderListByAncestorAscendent();
  else if (!strcmp(ORDERWAY, "DESCENDENT")) outlistAncestorStored->OrderListByAncestorDescendent();

  clusterFile.open(NAMECLUSTERT, fstream::out);
  outlistAncestorStored->ListAncestor()->SetInitial();

  while (outlistAncestorStored->ListAncestor()->SetNext()) {

    AncestorMol* currentAncestor = outlistAncestorStored->ListAncestor()->GetCurrent();
    currentAncestor->ListIdAncestor()->SetInitial();

    while (currentAncestor->ListIdAncestor()->SetNext()) {
      clusterFile << left;
      clusterFile.width(12);
      clusterFile << currentAncestor->ListIdAncestor()->GetCurrent()->Out() << " ";
    }

    clusterFile << endl;

  }

  outlistAncestorStored->ListAncestor()->TransferAll();

  clusterFile.close();

  if (listAncestorNew)        delete listAncestorNew;
  if (outlistAncestorStored)  delete outlistAncestorStored;

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ClusterByAPALINGO::prepareNewDB(char *inputNameFile) {

  if (!inputNameFile) return 0;

  ListAncestorMol* lisAncestorMol = new ListAncestorMol();

  lisAncestorMol->FilenameAncestor(inputNameFile);
  lisAncestorMol->FilenameSmiles(this->filename);

  lisAncestorMol->LoadDataFiles();

  lisAncestorMol->WriteSmilesAncestorFile(2, NAMEDBSMITMP);

  if (lisAncestorMol) delete lisAncestorMol;

  this->filename->In(NAMEDBSMITMP);

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ClusterByAPALINGO::AnalizeConvergence(int iteration, double &convergenceValue) {

  char command1[SIZENAMEFILE], command2[SIZENAMEFILE];

  convergenceValue = 0;
  
  sprintf(command1, "cp %s \0", NAMECLUSTERT); sprintf(command2, FORMATCLUST, iteration);
  sprintf(command1, "%s%s\0", command1, command2); system(command1);
  sprintf(command1, "rm %s\0", NAMECLUSTERT);      system(command1);
  
  if (!iteration) return false;

  ListAncestorMol* lisAncestorMol = new ListAncestorMol();

  sprintf(command1, FORMATCLUST, iteration);
  sprintf(command2, FORMATCLUST, iteration - 1);

  String namefileAncestor(command1);
  String namefileReferenceAncestor(command2);
  
  lisAncestorMol->FilenameAncestor(&namefileAncestor);
  lisAncestorMol->LoadDataAncestorFiles();

  convergenceValue = lisAncestorMol->ComputeConvergenceAncestorFile(&namefileReferenceAncestor);

  return (convergenceValue >= PERCENTCONV)?true:false;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double ClusterByAPALINGO::AnalizeConvergenceParallel(int iteration) {

  char command1[SIZENAMEFILE], command2[SIZENAMEFILE];

  double convergenceValue = 0;

  if (!this->rank) {
    sprintf(command1, "cp %s \0", NAMECLUSTERT); sprintf(command2, FORMATCLUST, iteration);
    sprintf(command1, "%s%s\0", command1, command2); system(command1);
    sprintf(command1, "rm %s\0", NAMECLUSTERT);      system(command1);
  }

  if (!iteration) return 0;

  ListAncestorMol* lisAncestorMol = new ListAncestorMol();

  sprintf(command1, FORMATCLUST, iteration);
  sprintf(command2, FORMATCLUST, iteration - 1);

  String              namefileAncestor(command1);
  String              namefileReferenceAncestor(command2);

  long                begin            = 0;
  long                end              = 0;
  long                numberCompounds  = 0;
  
  if (!this->rank) {
    
    ProcessFileParallel *fileToProcess = new ProcessFileParallel();

    fileToProcess->Filename(&namefileAncestor);
    fileToProcess->GetVirtualFlagsAncestor(this->sizeRank, 2);

    for (int i = 1; i < this->sizeRank; i++) {

      begin = *(fileToProcess->Flags()->Get(i));
      end   = *(fileToProcess->Flags()->Get(i + 1));
      
      MPI_Send(&begin, 1, MPI_LONG, i, (240 + i), MPI_COMM_WORLD);
      MPI_Send(&end,   1, MPI_LONG, i, (280 + i), MPI_COMM_WORLD);
      
    }
    
    begin = *(fileToProcess->Flags()->Get((long) this->rank));
    end   = *(fileToProcess->Flags()->Get((long)(this->rank + 1)));

    if (fileToProcess) delete fileToProcess;
    
  }
  else {
    
    MPI_Recv(&begin, 1, MPI_LONG, 0, (240 + this->rank), MPI_COMM_WORLD, &this->stat);
    MPI_Recv(&end,   1, MPI_LONG, 0, (280 + this->rank), MPI_COMM_WORLD, &this->stat);
    
  }

  lisAncestorMol->FilenameAncestor(&namefileAncestor);
  lisAncestorMol->LoadDataAncestorFilesFrom(begin, end);

  convergenceValue = lisAncestorMol->ComputeSumConvergenceAncestorFile(&namefileReferenceAncestor, numberCompounds);

  if (this->rank) {
    
    MPI_Send(&numberCompounds,  1, MPI_LONG  , 0, (320 + this->rank), MPI_COMM_WORLD);
    MPI_Send(&convergenceValue, 1, MPI_DOUBLE, 0, (360 + this->rank), MPI_COMM_WORLD);
    
    MPI_Recv(&convergenceValue, 1, MPI_DOUBLE, 0, (400 + this->rank), MPI_COMM_WORLD, &this->stat);
      
  }
  else {

    long   numberCompoundsTMP  = 0;
    double convergenceValueTMP = 0;
    
    for (int i = 1; i < this->sizeRank; i++) {

      MPI_Recv(&numberCompoundsTMP,  1, MPI_LONG,   i, (320 + i), MPI_COMM_WORLD, &this->stat);
      MPI_Recv(&convergenceValueTMP, 1, MPI_DOUBLE, i, (360 + i), MPI_COMM_WORLD, &this->stat);

      numberCompounds  += numberCompoundsTMP;
      convergenceValue += convergenceValueTMP;
      
    }

    convergenceValue /= numberCompounds;
    
    for (int i = 1; i < this->sizeRank; i++)
      MPI_Send(&convergenceValue, 1, MPI_DOUBLE, i, (400 + i), MPI_COMM_WORLD);
    
  }

  if (lisAncestorMol) delete lisAncestorMol;

  return convergenceValue;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ClusterByAPALINGO::AnalizeConvergenceClusterSim(int iteration, double &convergenceValue) {

  char command1[SIZENAMEFILE], command2[SIZENAMEFILE];

  convergenceValue = 0;

  sprintf(command1, "cp %s \0", NAMECLUSTERT); sprintf(command2, FORMATCLUST, iteration);
  sprintf(command1, "%s%s\0", command1, command2); system(command1);
  sprintf(command1, "rm %s\0", NAMECLUSTERT);      system(command1);

  if (!iteration) return false;

  ListAncestorMol* lisAncestorMol = new ListAncestorMol();

  sprintf(command1, FORMATCLUST, iteration);
  sprintf(command2, FORMATCLUST, iteration - 1);

  String namefileAncestor(command1);
  String namefileReferenceAncestor(command2);

  lisAncestorMol->FilenameAncestor(&namefileAncestor);
  lisAncestorMol->LoadDataAncestorFiles();

  convergenceValue = lisAncestorMol->ComputeConvergenceClusterSim(&namefileReferenceAncestor);

  return (convergenceValue >= PERCENTCONV)?true:false;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double ClusterByAPALINGO::AnalizeConvergenceParallelClusterSim(int iteration) {

  char command1[SIZENAMEFILE], command2[SIZENAMEFILE];

  if (!this->rank) {
    sprintf(command1, "cp %s \0", NAMECLUSTERT); sprintf(command2, FORMATCLUST, iteration);
    sprintf(command1, "%s%s\0", command1, command2); system(command1);
    sprintf(command1, "rm %s\0", NAMECLUSTERT);      system(command1);
  }

  if (!iteration) return 0;

  ListAncestorMol* lisAncestorMol = new ListAncestorMol();

  sprintf(command1, FORMATCLUST, iteration);
  sprintf(command2, FORMATCLUST, iteration - 1);

  String              namefileAncestor(command1);
  String              namefileReferenceAncestor(command2);

  long                begin            = 0;
  long                end              = 0;

  if (!this->rank) {

    ProcessFileParallel *fileToProcess = new ProcessFileParallel();

    fileToProcess->Filename(&namefileAncestor);
    fileToProcess->GetVirtualFlagsAncestor(this->sizeRank, 2);

    for (int i = 1; i < this->sizeRank; i++) {

      begin = *(fileToProcess->Flags()->Get(i));
      end   = *(fileToProcess->Flags()->Get(i + 1));

      MPI_Send(&begin, 1, MPI_LONG, i, (240 + i), MPI_COMM_WORLD);
      MPI_Send(&end,   1, MPI_LONG, i, (280 + i), MPI_COMM_WORLD);

    }

    begin = *(fileToProcess->Flags()->Get((long) this->rank));
    end   = *(fileToProcess->Flags()->Get((long)(this->rank + 1)));

    if (fileToProcess) delete fileToProcess;

  }
  else {

    MPI_Recv(&begin, 1, MPI_LONG, 0, (240 + this->rank), MPI_COMM_WORLD, &this->stat);
    MPI_Recv(&end,   1, MPI_LONG, 0, (280 + this->rank), MPI_COMM_WORLD, &this->stat);

  }

  lisAncestorMol->FilenameAncestor(&namefileAncestor);
  lisAncestorMol->LoadDataAncestorFilesFrom(begin, end);

  double factorRef        = 0;
  double factorOwn        = 0;
  double convergenceValue = 0;

  lisAncestorMol->ComputeSumConvergenceClusterSim(&namefileReferenceAncestor, factorRef, factorOwn);

  if (this->rank) {

    MPI_Send(&factorRef, 1, MPI_DOUBLE, 0, (320 + this->rank), MPI_COMM_WORLD);
    MPI_Send(&factorOwn, 1, MPI_DOUBLE, 0, (360 + this->rank), MPI_COMM_WORLD);

    MPI_Recv(&convergenceValue, 1, MPI_DOUBLE, 0, (400 + this->rank), MPI_COMM_WORLD, &this->stat);

  }
  else {

    double factorRefTMP  = 0;
    double factorOwnTMP = 0;

    for (int i = 1; i < this->sizeRank; i++) {

      MPI_Recv(&factorRefTMP, 1, MPI_DOUBLE, i, (320 + i), MPI_COMM_WORLD, &this->stat);
      MPI_Recv(&factorOwnTMP, 1, MPI_DOUBLE, i, (360 + i), MPI_COMM_WORLD, &this->stat);

      factorRef += factorRefTMP;
      factorOwn += factorOwnTMP;

    }

    convergenceValue = factorRef/factorOwn;

    for (int i = 1; i < this->sizeRank; i++)
      MPI_Send(&convergenceValue, 1, MPI_DOUBLE, i, (400 + i), MPI_COMM_WORLD);

  }

  if (lisAncestorMol) delete lisAncestorMol;

  return convergenceValue;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ClusterByAPALINGO::ClusteringDatabaseParallel(int argc, char** argv) {

  if (!this->filename->Length()) return false;

  //Set and Initializate the variables
  this->initialTime = time(NULL);
  this->err         = MPI_Init(&argc, &argv);

  if (this->err != MPI_SUCCESS) return false;

  MPI_Comm_rank(MPI_COMM_WORLD, &this->rank);
  MPI_Comm_size(MPI_COMM_WORLD, &this->sizeRank);
  
  long    totalNumberMol;
  int     nconvergence = 0;
  
  bool    convergence  = false;
  bool    logWrite     = (!this->rank && this->condLogFile);

  char    timeText[300];

  String  contFile;

  //Prepare the Log File of the process
  fstream logFile;

  //Continue the process of clustering (stoped before for any reason) using the argument mode=continue
  //Must be exist the file Cluster_Molecules%03d.txt\0
  if (argc > 2) {

    contFile.In(argv[2]);

    if (contFile.Contain("cont=")) {
      ///////123456789012345678901234//
      //cont=Cluster_MoleculesXXX.txt//
      char  tmpArgv[100]; strcpy(tmpArgv, argv[2]);
      char* part = strtok(tmpArgv, "="); part = strtok(NULL, "=");
      if (part) {
        
        strcpy(tmpArgv, part); part = strtok(tmpArgv, ".");
        if (part)
          nconvergence = atoi((part + 17));

        *(this->initialFileName) = *(this->filename);

        if (logWrite) {
          logFile.open(this->nameLogFile->Out(), fstream::out | fstream::app);
          logWrite = (logFile.is_open() && logWrite);
        }

      }

    }

  }
  else {
    if (logWrite) {
      logFile.open(this->nameLogFile->Out(), fstream::out);
      logWrite = (logFile.is_open() && logWrite);
    }
  }

  if (logWrite) {
    logFile << "(" << this->ShowTime(timeText) << ")" << endl;
    logFile << "----------------------------------------------------------------" << endl;
    logFile << "#        The initial DB file is: "   << this->filename->Out()     << endl;
    logFile << "----------------------------------------------------------------" << endl;
    logFile << "#                 Clustering Process Starting                   " << endl;
    logFile << "----------------------------------------------------------------" << endl;
    logFile << "#        Number of Parallel Process (" << this->sizeRank << ")  " << endl;
    logFile << "----------------------------------------------------------------" << endl;
  }

  //Initiate the clustering algorithm
  while (!convergence && (nconvergence < this->maxIterConv)) {                  //Repeat until the clustering process will converge

    if (!this->rank) cout << " (" << this->ShowTime(timeText) << ") " << "Iteration: " << nconvergence << endl;

    MPI_Barrier(MPI_COMM_WORLD);
    
    if (!this->rank && nconvergence) *(this->filename) = *(this->initialFileName);
    
    if (logWrite)     
      logFile << "Iteration: " << nconvergence << " (" << this->ShowTime(timeText) << ")" << endl;

    for (int ancestor = 0; ancestor < NANCESTOR; ancestor++) {                  //Obtain the ancestor in each process until ANCESTOR level

      //Set the name for the working File
      if (!this->rank) {                                                        //Master Process

        if (logWrite) 
          logFile << "Ancestor Level: " << ancestor + 1 << " (" << this->ShowTime(timeText) << ")" << endl;

        cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Ancestor Level: " << ancestor << endl;

        int lengthFileName = this->filename->Length();
        for (int i = 1; i < this->sizeRank; i++) {
          MPI_Send(&lengthFileName,                    1,  MPI_INT, i,        i, MPI_COMM_WORLD);
          MPI_Send(this->filename->Out(), lengthFileName, MPI_CHAR, i, (40 + i), MPI_COMM_WORLD);
        }
        
      }
      else {                                                                    //Worker Process

        //Receive the filename variable
        int  lengthFileName;
        MPI_Recv(&lengthFileName,	1,  MPI_INT, 0, this->rank, MPI_COMM_WORLD, &this->stat);
        
        char recvFilename[lengthFileName + 1];
        MPI_Recv(recvFilename, lengthFileName, MPI_CHAR, 0, (40 + this->rank), MPI_COMM_WORLD, &this->stat);
        recvFilename[lengthFileName] = '\0';
        this->filename->In(recvFilename);
        
      }

      this->molDB->Filename(this->filename);

      if (!this->rank) {                                                        //Prepare the input File (Only in the Master Process)

        if (!nconvergence && !ancestor) {                                       //First execution of algorithm for all ancestral level (Ramdon Sequences Order)

          *(this->initialFileName) = *(this->filename);

          if (READFILE) {
            if (logWrite) logFile << "Read sequences in Original Order" << " (" << this->ShowTime(timeText) << ")" << endl;
            cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Read sequences in Original Order" << endl;
            this->molDB->ReadFile();
          }
          else {
            if (logWrite) logFile << "Order the sequences Ramdon" << " (" << this->ShowTime(timeText) << ")" << endl;
            cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Order the sequences Ramdon" << endl;
            this->molDB->RandomFile(ITERATIONS);
          }

          totalNumberMol = this->molDB->AllSmiles()->Length();
          this->molDB->AllSmiles()->Clear();

        }
        else if (nconvergence && !ancestor) {                                   //Execution of algorithm for the first ancestral level after the first execution (Sequences Ordering by level of ancestral)

          if (logWrite) logFile << "Order the sequences by the Previos Clustering" << " (" << this->ShowTime(timeText) << ")" << endl;
          char   nameFileClMol[SIZENAMEFILE]; sprintf(nameFileClMol, FORMATCLUST, nconvergence - 1);
          String nameFileClusterMol(nameFileClMol);
          String orderWay(ORDERWAY);
          cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Order the sequences by the Previos Clustering" << endl;
          totalNumberMol = this->molDB->OrderFile(this->filename, &nameFileClusterMol, NANCESTOR, &orderWay);

        }
        else {                                                                  //Execution of algorithm fot the level different of first ancestral level after the first execution (Sequences Ordering keeping the order of previous levels)

          if (logWrite) logFile << "Order the sequences keeping the order in the Previos Clustering" << " (" << this->ShowTime(timeText) << ")" << endl;
          String nameFileClusterMol(NAMECLUSTERT);
          cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Order the sequences keeping the order in the Previos Clustering" << endl;
          totalNumberMol = this->molDB->ReduceFile(this->filename, &nameFileClusterMol);

        }

        for (int i = 1; i < this->sizeRank; i++)
          MPI_Send(&totalNumberMol, 1,  MPI_LONG, i, (80 + i), MPI_COMM_WORLD);

      }
      else
        MPI_Recv(&totalNumberMol, 1,  MPI_LONG, 0, (80 + this->rank), MPI_COMM_WORLD, &this->stat);

      //Clustering process
      if (logWrite) logFile << "Clustering Process Starting" << " (" << this->ShowTime(timeText) << ")" << endl;

      int stateWork = this->ClusteringAPA(totalNumberMol);

      if (logWrite) {
        logFile << "Finishing the Clustering Process" << " (" << this->ShowTime(timeText) << ")" << endl;
        logFile << "Synchronization" << " (" << this->ShowTime(timeText) << ")" << endl;
      }
      
      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Finishing the Clustering Process" << endl;
      cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Synchronization" << endl;

      MPI_Barrier(MPI_COMM_WORLD);

      //Integrate the obtained result for each node (if the node work with any data)
      if (stateWork) {

        if (!this->rank) {

          fstream fileResult;
          char    resultFile[SIZENAMEFILE];
          sprintf(resultFile, FORMATRESULT, this->rank);
          fileResult.open(resultFile, fstream::out | fstream::app);
          fileResult.seekg (0, fstream::end);

          long  length = 0;
          char *buffer = NULL;

          for (int i = 1; i < this->sizeRank; i++) {
            
            MPI_Recv(&length,      1, MPI_LONG, i, (120 + i), MPI_COMM_WORLD, &this->stat);
            buffer = new char[length + 1];
            
            MPI_Recv(buffer , length, MPI_CHAR, i, (160 + i), MPI_COMM_WORLD, &this->stat);
            buffer[length] = '\0';
            fileResult << buffer;
            
            if (buffer) delete[] buffer;
            
          }

          fileResult.close();

          cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Joining the Cluster Result" << endl;
          if (logWrite) logFile << "Joining the Cluster Result" << " (" << this->ShowTime(timeText) << ")" << endl;
          this->JoinClusterResult(resultFile, ancestor);
          
          cout << " (" << this->ShowTime(timeText) << ") Process: " << this->rank << " - Preparing the new Data Set" << endl;
          if (logWrite) logFile << "Preparing the new Data Set" << " (" << this->ShowTime(timeText) << ")" << endl;
          this->prepareNewDB(resultFile);

          char command[SIZENAMEFILE]; sprintf(command, "rm %s\0", resultFile); system(command);

        }
        else {

          fstream fileResult;
          char    resultFile[SIZENAMEFILE];
          sprintf(resultFile, FORMATRESULT, this->rank);
          fileResult.open(resultFile, fstream::in);

          fileResult.seekg (0, fstream::end);
          long length = fileResult.tellg();
          fileResult.seekg (0, fstream::beg);
          char *buffer = new char[length + 1];

          //Read data as a block:
          fileResult.read(buffer, length);
          fileResult.close();

          MPI_Send(&length,      1, MPI_LONG, 0, (120 + this->rank), MPI_COMM_WORLD);
          MPI_Send(buffer , length, MPI_CHAR, 0, (160 + this->rank), MPI_COMM_WORLD);

          if (buffer) delete[] buffer;

          char command[SIZENAMEFILE]; sprintf(command, "rm %s\0", resultFile); system(command);

        }

      }
      else {

        long  length = 0;
        char *buffer = new char[1];

        MPI_Send(&length,      1, MPI_LONG, 0, (120 + rank), MPI_COMM_WORLD);
        MPI_Send(buffer , length, MPI_CHAR, 0, (160 + rank), MPI_COMM_WORLD);

        if (buffer) delete[] buffer;

      }

      MPI_Barrier(MPI_COMM_WORLD);

    } //end for

    if (!this->rank) {
      char command[SIZENAMEFILE]; sprintf(command, "rm %s\0", this->filename->Out()); system(command);
      if (logWrite) logFile << "Analysis of Convergence" << " (" << this->ShowTime(timeText) << ")" << endl;
    }

    double convergenceValue = this->AnalizeConvergenceParallelClusterSim(nconvergence);
    convergence = (convergenceValue >= PERCENTCONV)?true:false;

    if (!this->rank) {
      if (logWrite) logFile << "Convergence = " << convergenceValue << " (Iteration: " << nconvergence << ")" << endl;
      cout << " (" << this->ShowTime(timeText) << ")" << " Convergence = " << convergenceValue << " (Iteration: " << nconvergence << ")" << endl;
    }

    MPI_Barrier(MPI_COMM_WORLD);

    nconvergence++;

  } //end while

  MPI_Finalize();

  if (logWrite) {
    logFile << "Parallel Clustering Process Finished" << " (" << this->ShowTime(timeText) << ")" << endl;
    logFile.close();
  }

  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* ClusterByAPALINGO::ShowTime(char* timeText) {
  
  time (&this->finalTime);
  
  int timeSeconds = (int)difftime(this->finalTime, this->initialTime);
  int timeMinutes = (timeSeconds/60);
  int timeHours   = (timeMinutes/60);
  int timeDays    = (timeHours/24);

  timeSeconds    %= 60;
  timeMinutes    %= 60;
  timeHours      %= 24;

  sprintf(timeText, "Elapsed Time: %02d:%02d:%02d:%02d (DD:HH:MM:SS)\0", timeDays, timeHours, timeMinutes, timeSeconds);

  return timeText;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
